import utils from '../common/utils'
import formItemMixin from './formItemMixin'
import dayjs from 'dayjs'

export default {
    mixins: [formItemMixin],
    emits: ['update:value', 'validate'],
    props: {
        rules: {
            type: Array,
            default: []
        },
        /**
         * 是否是内部组件，内部组件不会响应规则验证
         */
        isInner: {
            type: Boolean,
            default: false,
        },
    },
    data() {
        return {
            validator: {
                changeId: 0,
                valid: null,
                errorMessage: null,
            },
            keyForForm: Symbol()
        }
    },
    watch: {
        value() {
            if (this.isInner) {
                return
            }
            this.validator.changeId++
        },
        rules: {
            handler() {
                if (this.isInner) {
                    return
                }
                this.validator.changeId++
                this.setInputRequired()
            },
            deep: true
        },
        'validator.changeId'() {
            if (this.isInner) {
                return
            }
            this.validator.valid = true
            this.validator.errorMessage = null
            this.validate(this.validator.changeId, this.getValueToValidate())
        },
        'validator.valid'() {
            if (this.isInner) {
                return
            }
            this.$emit('validate', [this.validator.valid, this.validator.errorMessage])
            this.formItem?.setValidateResult(this.validator.valid, this.validator.errorMessage)
        }
    },
    mounted() {
        if (this.isInner) {
            return
        }
        this.form?.addValidateFunc(this.keyForForm, this.isValid)
        this.setInputRequired()
    },
    methods: {
        getValueToValidate() {
            return this.value
        },
        validate(changeId, val) {
            if (this.isInner) {
                return
            }
            this.rules.forEach(async rule => {
                if (changeId != this.validator.changeId) {
                    return
                }
                if (rule.delay > 0) {
                    await utils.sleep(rule.delay)
                    if (changeId != this.validator.changeId) {
                        return
                    }
                }
                let validateFunc = this['validateFunc_' + rule.name]
                if (typeof validateFunc !== 'function') {
                    return
                }
                let result = await validateFunc(rule, val)
                if (result == null) {
                    return
                }
                let [valid, errorMessage] = result
                if (valid) {
                    return
                }
                if (changeId != this.validator.changeId) {
                    return
                }
                if (this.validator.valid !== false) {
                    this.validator.valid = false
                    this.validator.errorMessage = errorMessage
                }
            })
        },
        async isValid() {
            if (this.isInner) {
                return true
            }
            let ps = this.rules.map(rule => {
                return new Promise(async resolve => {
                    let validateFunc = this['validateFunc_' + rule.name]
                    if (typeof validateFunc !== 'function') {
                        return resolve([true])
                    }
                    let result = await validateFunc(rule, this.value)
                    if (result == null) {
                        return resolve([true])
                    }
                    return resolve(result)
                })
            })
            let results = await Promise.all(ps)
            let invalidResult = results.find(a => !a[0])
            if (invalidResult) {
                if (this.validator.valid !== false) {
                    this.validator.valid = false
                    this.validator.errorMessage = invalidResult[1]
                }
                return false
            }
            return true
        },
        validateFunc_required(rule, val) {
            if (!rule.value) {
                return [true]
            }
            if (val == null || val === '') {
                return [false, rule.errorMessage || this.$t('inputMixin.Required')]
            }
            return [true]
        },
        validateFunc_minLength(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (val.length >= rule.value) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.MinLength', [rule.value])]
        },
        validateFunc_maxLength(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (val.length <= rule.value) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.MaxLength', [rule.value])]
        },
        validateFunc_pattern(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (rule.value.test(val)) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.InValidFormat')]
        },
        validateFunc_min(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (val >= rule.value) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.Min', [rule.value])]
        },
        validateFunc_max(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (val <= rule.value) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.Max', [rule.value])]
        },
        validateFunc_decimal(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            if (utils.getDecimalCount(val) <= rule.value) {
                return [true]
            }
            let defaultErrorMessage = rule.value == 0 ? this.$t('inputMixin.MustBeInteger') : this.$t('inputMixin.Decimal', [rule.value])
            return [false, rule.errorMessage || defaultErrorMessage]
        },
        validateFunc_earliest(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            let dt = rule.value instanceof Date ? rule.value : new Date(rule.value)
            if (val >= dt) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.Earliest', [dayjs(rule.value).format(this.format)])]
        },
        validateFunc_latest(rule, val) {
            if (val == null || val === '') {
                return [true]
            }
            let dt = rule.value instanceof Date ? rule.value : new Date(rule.value)
            if (val <= dt) {
                return [true]
            }
            return [false, rule.errorMessage || this.$t('inputMixin.Latest', [dayjs(rule.value).format(this.format)])]
        },
        async validateFunc_func(rule, val) {
            let validResult = await rule.value(val)
            if (typeof validResult == 'boolean') {
                if (validResult) {
                    return [true]
                }
                return [false, rule.errorMessage ?? this.$t('inputMixin.NonConformance')]
            } else if (validResult instanceof Array) {
                let [valid, errorMessage] = validResult
                if (valid) {
                    return [true]
                }
                return [false, errorMessage ?? rule.errorMessage ?? this.$t('inputMixin.NonConformance')]
            } else {
                console.warn(this.$t('inputMixin.FuncWarnning'))
            }
        },
        setInputRequired() {
            if (this.isInner) {
                return
            }
            let required = this.rules.find(a => a.name == 'required')?.value ?? false
            this.formItem?.setInputRequired(required)
        },
    },
    unmounted() {
        if (this.isInner) {
            return
        }
        this.form?.removeValidateFunc(this.keyForForm)
    }
}